// Package check3 实现语义检查
package check3

import (
	"fmt"
	"strconv"
	"strings"

	"gitee.com/u-language/u-language/ucom/ast2"
	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

var Debug = false
var Test = false

func CheckTree(t *ast2.Tree) {
	left := 0
	for i := 0; i < len(t.Nodes); i++ {
		if t.CheckInfo.SwitchSkip {
			switch t.Nodes[i].(type) {
			case ast2.CodeBlockIface:
				left++
			case ast2.RbraceNode:
				left--
				if left <= 0 {
					t.CheckInfo.SwitchSkip = false
				}
			}
			continue
		}
		checkNode(t, t.Nodes[i])
	}
}

// checkNode 检查一个语法节点
// 如果检查的是一个表达式，返回表达式的类型
func checkNode(t *ast2.Tree, n ast2.Node) (err errcode.ErrCode, msg errcode.Msg, typ string) {
	switch v := n.(type) {
	case *ast2.VarNode:
		checkVar(t, v)
	case astdata.Typ:
		return checkType(t, v)
	case *ast2.CallNode:
		err, typ = checkCall(t, v)
		return err, nil, typ
	case *ast2.ASSIGNNode:
		checkAssign(t, v)
	case *ast2.ReturnNode:
		checkReturn(t, v)
	case *ast2.OpExpr:
		return checkOpExpr(t, v)
	case *ast2.IndexExpr:
		err, typ = checkIndexExpr(t, v)
		return err, nil, typ
	case *ast2.FuncNode:
		t.Funcinfo = v.FuncInfo
		t.PushNewSbt(v.Sbt)
		checkFunc(t, v.FuncInfo)
	case *ast2.MethodNode:
		t.Funcinfo = v.FuncInfo
		t.PushNewSbt(v.Sbt)
		checkFunc(t, v.FuncInfo)
	case ast2.RbraceNode:
		t.SubRbrace()
	case *ast2.IfNode:
		t.PushNewSbt(v.Sbt)
		checkIf(t, v)
	case *ast2.ElseNode:
		t.PushNewSbt(v.Sbt)
		checkElse(t, v)
	case *ast2.ForNode:
		t.PushNewSbt(v.Sbt)
		checkFor(t, v)
	case *ast2.SwitchNode:
		t.CheckInfo.SwitchSkip = checkSwitch(t, v)
		if !t.CheckInfo.SwitchSkip {
			t.PushNewSbt(v.Sbt)
		}
	case *ast2.CaseNode:
		t.PushNewSbt(v.Sbt)
		checkCase(t, v)
	case *ast2.DefaultNode:
		t.PushNewSbt(v.Sbt)
	case *ast2.SelfOpStmt:
		checkSelfOpStmt(t, v)
	case *ast2.LabelNode:
		if i := index(t.CheckInfo.Label, v.Value); i != -1 { //如果在函数内两个相同的标签
			t.Panic(v.LineNum, errcode.NewMsgSymbolRepeat(v.Value, t.Filename, t.Filename, v.LineNum, t.CheckInfo.Label[i].LineNum), errcode.LabelErr, errcode.SymbolRepeat)
			return
		}
		t.CheckInfo.Label = append(t.CheckInfo.Label, v)
	case *ast2.GotoStmt:
		if i := index(t.CheckInfo.Label, v.Label); i == -1 { //如果当前函数内没有goto语句要跳转的标签，先把这个goto语句记录下来，因为这个标签可能在后面
			t.CheckInfo.Goto = append(t.CheckInfo.Goto, v)
		}
	case ast2.BreakStmt, ast2.ContinueStmt:
	case *ast2.ConstNode:
		checkConst(t, v)
	default:
		if Debug {
			panic(n)
		}
	}
	return errcode.NoErr, nil, ""
}

func index(s []*ast2.LabelNode, str string) int {
	for i := range s {
		if s[i].Value == str {
			return i
		}
	}
	return -1
}

// checkType 检查一个类型节点，并类型的名称
func checkType(t *ast2.Tree, n astdata.Typ) (err errcode.ErrCode, msg errcode.Msg, typ string) {
	switch v := n.(type) {
	case *ast2.Object:
		return retType(v, t.Sbt, t)
	case *ast2.Array:
		return errcode.NoErr, nil, v.Typ()
	case *ast2.StructDecl:
		t.PushNewSbt(t.Sbt)
		return errcode.NoErr, nil, v.Name
	case *ast2.Objects:
		err, msg, typ, _ = checkSelector(t, v, nil)
		return
	case *ast2.EnumDecl:
		return errcode.NoErr, nil, v.Name
	}
	if Debug {
		panic(n)
	}
	return errcode.NoErr, nil, ""
}

// retType获取Object的类型，对于未知的，将panic
//   - ptr是被获取类型的的Object,不能为nil
//   - sbt是Object所在区块的符号表,不能为nil
func retType(ptr *ast2.Object, sbt *ast2.Sbt, t *ast2.Tree) (errcode.ErrCode, errcode.Msg, string) {
	switch ptr.Kind {
	case ast2.INTOBJ: //如果是int
		return errcode.NoErr, nil, enum.Int
	case ast2.FLOATOBJ: //如果是float
		return errcode.NoErr, nil, enum.Float
	case ast2.BoolObj: //如果是bool
		return errcode.NoErr, nil, enum.Bool
	case ast2.StringObj: //如果是字符串
		return errcode.NoErr, nil, enum.String
	case ast2.SymbolObj, ast2.StructPtr | ast2.SymbolObj: //如果是符号
		v, err := sbt.Have(ptr.Name)
		if err != errcode.NoErr {
			return errcode.UnknownSymbol, errcode.NewMsgUnknownSymbol(ptr.Name), ""
		}
		switch v := v.(type) {
		case astdata.Typ:
			return errcode.NoErr, nil, v.Typ()
		case *ast2.VarNode:
			return errcode.NoErr, nil, v.TYPE.Typ()
		case *ast2.ConstNode:
			t.CheckInfo.IsConst = true
			return errcode.NoErr, nil, v.TYPE.Typ()
			// return errcode.UnknownSymbol, errcode.NewMsgUnknownSymbol(ptr.Name), ""
		}
		fallthrough
	case ast2.SymbolObj | ast2.TypeObj, ast2.TypeObj: //如果是类型
		v, err := sbt.HaveType(ptr.Name)
		if err != errcode.NoErr {
			return errcode.UnknownType, errcode.NewMsgUnknownSymbol(ptr.Name), ""
		}
		return errcode.NoErr, nil, v.Typ()
	case ast2.NilObj: //如果是指针的零值
		return errcode.NoErr, nil, enum.Nil
	case ast2.LeaObj, ast2.LeaObj | ast2.TypeObj: //如果是取地址
		_, ok := ast2.TypeEnumStrMap[ptr.Name]
		if ok {
			return errcode.NoErr, nil, "&" + ptr.Name
		}
		vv, err := sbt.Have(ptr.Name)
		if err != errcode.NoErr {
			return err, errcode.NewMsgUnknownSymbol(ptr.Name), ""
		}
		switch v := vv.(type) {
		case astdata.Typ:
			return errcode.NoErr, nil, "&" + v.Typ()
		case *ast2.VarNode:
			return errcode.NoErr, nil, "&" + v.TYPE.Typ()
		case *ast2.ConstNode:
			return errcode.NoErr, nil, "&" + v.TYPE.Typ()
		default:
			return errcode.UnknownSymbol, errcode.NewMsgUnknownSymbol(ptr.Name), ""
		}
	case ast2.DerefObj: //如果是解引用
		vv, err := sbt.Have(ptr.Name)
		if err != errcode.NoErr {
			return errcode.UnknownSymbol, errcode.NewMsgUnknownSymbol(ptr.Name), ""
		}
		switch v := vv.(type) {
		case *ast2.VarNode:
			typ := v.TYPE.Typ()
			if typ[0] != '&' {
				return errcode.OnlyTypePtrCanDeref, nil, ""
			}
			return errcode.NoErr, nil, typ[1:]
		case *ast2.ConstNode:
			typ := v.TYPE.Typ()
			if typ[0] != '&' {
				return errcode.OnlyTypePtrCanDeref, nil, ""
			}
			return errcode.NoErr, nil, typ[1:]
		default:
			//如果既不是解引用变量，也不是解引用常量
			return errcode.UnrefNoVarAndConst, nil, ""
		}
	}
	panic(fmt.Errorf("未知的类型 ptr=%+v \n sbt=%+v", ptr, sbt))
}

func checkVar(t *ast2.Tree, v *ast2.VarNode) {
	if v.Value == nil {
		return
	}
	err, msg, vtyp := checkNode(t, v.Value)
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, err)
		return
	}
	err, msg, ttyp := checkType(t, v.TYPE)
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, err)
		return
	}
	if !utils.Typ_is_equal(vtyp, ttyp) {
		t.Panic(v.LineNum, errcode.NewMsgAssignTypeIsNotEqual(ttyp, vtyp), errcode.VARErr, errcode.TypeIsNotEqual)
	}

	// 如果存在变量遮蔽
	// 对类似这类代码
	// var a int = 1
	// if true {
	//	var a int = a
	//}
	// 重写为
	// var a int = 1
	// if true {
	//	var tmp int = a
	//	var a int = tmp
	//}
	//TODO:以后后端支持并使用llvm ir，可以禁用这里
	if o, ok := v.Value.(*ast2.Object); ok && o.Name == v.Name {
		t.CheckInfo.TmpInt++
		tmp := ast2.NewVarNode("tmp"+strconv.Itoa(t.CheckInfo.TmpInt), true, v.LineNum)
		tmp.TYPE = v.TYPE
		tmp.Value = ast2.NewObject(ast2.SymbolObj, o.Name)
		o.Name = tmp.Name
		v.Tmp = tmp
	}
}

func checkConst(t *ast2.Tree, v *ast2.ConstNode) {
	err, msg, vtyp := checkNode(t, v.Value)
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, err)
		return
	}
	err, msg, ttyp := checkType(t, v.TYPE)
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, err)
		return
	}
	if !utils.Typ_is_equal(vtyp, ttyp) {
		t.Panic(v.LineNum, errcode.NewMsgAssignTypeIsNotEqual(ttyp, vtyp), errcode.ConstErr, errcode.TypeIsNotEqual)
	}
}

func checkCall(t *ast2.Tree, v *ast2.CallNode) (err errcode.ErrCode, typ string) {
	var msg errcode.Msg
	var info *ast2.FuncInfo
	off := 0
	err, msg, v.CCallFuncName, info = getFuncName(t, v.FuncName, &off)
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, errcode.CallErr, err)
		return ignore, ""
	}
	if info == nil {
		t.Panic(v.LineNum, nil, errcode.CallErr, errcode.CallNoFunc)
		return ignore, ""
	}
	if ok, typ, err := predefined_func(t, v, info); ok {
		return err, typ
	}
	err, msg = check_parame_num(v.CCallFuncName, len(v.Parame)+off, len(info.Parame))
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, errcode.CallErr, err)
		return ignore, ""
	}
	for i, p := range v.Parame {
		err, msg, typ := checkNode(t, p)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			continue
		}
		err, msg, decltyp := checkType(t, info.Parame[i+off].Type)
		if err != errcode.NoErr {
			if Debug {
				panic(info.String())
			}
			//Note:函数声明的错误应该在检查函数时报错
			continue
		}
		if !utils.Typ_is_equal(typ, decltyp) {
			t.Panic(v.LineNum, errcode.NewMsgCallTypeIsNotEqual(decltyp, typ, i+1), errcode.CallErr, errcode.TypeIsNotEqual)
		}
	}
	if len(info.RetValue) != 0 {
		return errcode.NoErr, info.RetValue[0].Type.Typ()
	}
	return
}

var ignore = errcode.ErrCode(10000)

// predefined_func 检查预定义的函数或视为函数调用的类型转换
func predefined_func(t *ast2.Tree, v *ast2.CallNode, info *ast2.FuncInfo) (yes bool, typ string, err errcode.ErrCode) {
	switch info.Name {
	case enum.Malloc:
		err, msg := check_parame_num(enum.Malloc, len(v.Parame), 1)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		tn, ok := v.Parame[0].(astdata.Typ)
		if !ok {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.ParameOfMallocAType)
			return true, "", ignore
		}
		err, msg, typ = checkType(t, tn)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		typ = "&" + typ
	case enum.MallocSize:
		err, msg := check_parame_num(enum.MallocSize, len(v.Parame), 1)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		err, msg, typ = checkNode(t, v.Parame[0])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if typ != enum.Int {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.ParameForMallocSizeMustInt)
			return true, "", ignore
		}
		typ = enum.UnsafePointer
	case enum.Printf:
		if len(v.Parame) == 0 {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.InsuParame)
			return true, "", ignore
		}
		for i, p := range v.Parame {
			err, msg, typ := checkNode(t, p)
			if err != errcode.NoErr {
				t.Panic(v.LineNum, msg, err)
				continue
			}
			if i == 0 && typ != enum.String {
				t.Panic(v.LineNum, nil, errcode.CallErr, errcode.FirstParameOfPrinfASting)
			}
		}
	case enum.Int:
		err, msg := check_parame_num(enum.Int, len(v.Parame), 1)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		err, msg, Typ := checkNode(t, v.Parame[0])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, err)
			return true, "", ignore
		}
		if Typ != enum.Float && !Test {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.CanOnlyConvertFloatToInt)
		}
		typ = enum.Int
	case enum.Float:
		err, msg := check_parame_num(enum.Float, len(v.Parame), 1)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		err, msg, Typ := checkNode(t, v.Parame[0])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, err)
			return true, "", ignore
		}
		if Typ != enum.Int {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.CanOnlyConvertIntToFloat)
		}
		typ = enum.Float
	case enum.UnsafeConvert:
		err, msg := check_parame_num(enum.UnsafeConvert, len(v.Parame), 2)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		err, msg, typ = checkNode(t, v.Parame[0])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if !utils.IsPtr(typ) {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.FirstParameOfUnsafeConvertAPtr)
			return true, "", ignore
		}
		err, msg, typ = checkNode(t, v.Parame[1])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if !utils.IsPtr(typ) {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.SecondParameOfUnsafeConvertAPtrType)
			return true, "", ignore
		}
	case enum.UnsafeSizeof:
		err, msg := check_parame_num(enum.UnsafeSizeof, len(v.Parame), 1)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		T, ok := v.Parame[0].(astdata.Typ)
		if !ok {
			t.Panic(v.LineNum, msg, errcode.CallErr, errcode.FirstParameOfUnsafeSizeAType)
			return true, "", ignore
		}
		err, msg, typ = checkType(t, T)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if typ == "" {
			t.Panic(v.LineNum, msg, errcode.CallErr, errcode.FirstParameOfUnsafeSizeAType)
			return true, "", ignore
		}
		typ = enum.Int
	case enum.UnsafeAdd:
		err, msg := check_parame_num(enum.UnsafeAdd, len(v.Parame), 2)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		err, msg, typ = checkNode(t, v.Parame[0])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if !utils.IsPtr(typ) {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.FirstParameOfUnsafeAddAPtr)
			return true, "", ignore
		}
		err, msg, typ = checkNode(t, v.Parame[1])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if typ != enum.Int {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.SecondParameOfUnsafeAddAnInt)
			return true, "", ignore
		}
		typ = enum.UnsafePointer
	case enum.Free:
		err, msg := check_parame_num(enum.Free, len(v.Parame), 1)
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		err, msg, typ := checkNode(t, v.Parame[0])
		if err != errcode.NoErr {
			t.Panic(v.LineNum, msg, errcode.CallErr, err)
			return true, "", ignore
		}
		if !utils.IsPtr(typ) {
			t.Panic(v.LineNum, nil, errcode.CallErr, errcode.FreeArgMustPtr)
			return true, "", ignore
		}
	default:
		return false, "", errcode.NoErr
	}
	yes = true
	return
}

// 检查参数数量
//   - callernum 传递的参数数量
//   - calleenum 函数声明的参数数量
func check_parame_num(funcname string, callernum int, calleenum int) (code errcode.ErrCode, msg errcode.Msg) {
	if callernum > calleenum { //如果传递的参数数量大于函数声明的参数数量
		return errcode.TManyParame, errcode.NewMsgNumberOfParameNoMatch(callernum, calleenum, funcname)
	} else if callernum < calleenum { //如果传递的参数数量小于函数声明的参数数量
		return errcode.InsuParame, errcode.NewMsgNumberOfParameNoMatch(callernum, calleenum, funcname)
	}
	return errcode.NoErr, nil
}

func getFuncName(t *ast2.Tree, n ast2.Expr, off *int) (err errcode.ErrCode, msg errcode.Msg, callname string, funcinfo *ast2.FuncInfo) {
	switch v := n.(type) {
	case *ast2.Object:
		f, err := t.Sbt.Have(v.Name)
		if err != errcode.NoErr {
			return err, errcode.NewMsgUnknownSymbol(v.Name), "", nil
		}
		F, ok := f.(*ast2.FuncNode)
		if !ok {
			return errcode.CallNoFunc, nil, "", nil
		}
		return errcode.NoErr, nil, v.Name, F.FuncInfo
	case *ast2.Objects:
		return checkSelector(t, v, off)
	default:
		panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的类型 %T", n)))
	}
}

func checkAssign(t *ast2.Tree, n *ast2.ASSIGNNode) {
	err, msg, dtyp := checkNode(t, n.Dest)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.ASSIGNErr, err)
		return
	}
	if t.CheckInfo.IsConst {
		//TODO:支持选择器报错
		t.Panic(n.LineNum, errcode.NewMsgAssignToConst(n.Dest.(*ast2.Object).Name), errcode.ASSIGNErr)
		return
	}
	defer func() { t.CheckInfo.IsConst = false }()
	err, msg, styp := checkNode(t, n.Src)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.ASSIGNErr, err)
		return
	}
	if !utils.Typ_is_equal(dtyp, styp) {
		t.Panic(n.LineNum, errcode.NewMsgAssignTypeIsNotEqual(dtyp, styp), errcode.ASSIGNErr, errcode.TypeIsNotEqual)
	}
}

func checkSelector(t *ast2.Tree, v *ast2.Objects, off *int) (err errcode.ErrCode, msg errcode.Msg, typ string, info *ast2.FuncInfo) {
	defer func() {
		if v.Slice[0].Kind&ast2.LeaObj != 0 {
			typ = "&" + typ
		}
	}()
	vv, err := t.Sbt.Have(v.Slice[0].Name)
	if err != errcode.NoErr {
		return err, errcode.NewMsgUnknownSymbol(v.Slice[0].Name), "", nil
	}
	name := ""
	switch n := vv.(type) {
	case *ast2.VarNode:
		name = n.TYPE.Typ()
	case *ast2.ConstNode:
		name = n.TYPE.Typ()
	case *ast2.EnumDecl:
		name = n.Name
	case *ast2.StructDecl:
		name = n.Name
	case *ast2.Package:
		a, err := n.Sbt.Have(v.Slice[1].Name)
		if err != errcode.NoErr {
			return errcode.SelectorLNoSelectorR, errcode.NewMsgSelectorLNoSelectorR(n.PackageName, v.Slice[1].Name), "", nil
		}
		if f, ok := a.(*ast2.FuncNode); ok {
			info = f.FuncInfo
		}
		v.IsImportSymbol = true
		return errcode.NoErr, nil, "", info
	default:
		return errcode.UnknownSymbol, errcode.NewMsgUnknownSymbol(v.Slice[0].Name), "", nil
	}
	for i := 0; i < len(v.Slice)-1; i++ {
		err, msg, typ, info = check_selector_Left_right(t, v, &v.Slice[i], v.Slice[i+1].Name, off, &name)
		if err != errcode.NoErr {
			break
		}
	}
	return
}

// check_selector_Left_right 检查选择器左值是否包含右值
func check_selector_Left_right(t *ast2.Tree, v *ast2.Objects, left *ast2.Object, right string, off *int, leftTypName *string) (err errcode.ErrCode, msg errcode.Msg, typOrCallname string, info *ast2.FuncInfo) {
	var Typ any
	Typ, err = t.Sbt.HaveType(*leftTypName)
	switch s := Typ.(type) {
	case *ast2.StructDecl:
		//TODO:更好的返回错误
		field := s.FindField(right)
		if field.Name == "" {
			typ := s.Typ()
			m := t.Sbt.HaveMethod(typ, right)
			method, ok := m.(*ast2.MethodNode)
			if !ok {
				return errcode.SelectorLNoSelectorR, errcode.NewMsgSelectorLNoSelectorR(left.Name, right), "", nil
			}
			if off != nil {
				*off = 1
			}
			v.T = typ
			//如果出现这种代码
			//struct s{
			//p int
			//}
			// method f(v s)
			// var v s
			// v.f.p()
			//设置leftTypName为方法名，确保
			//检查f.p时，因为方法名在符号表不是记录为类型，所以会有正确的报错（左值f没有右值p）
			*leftTypName = method.Name
			//TODO:优化不必要的字符串拼接
			return errcode.NoErr, nil, astdata.Generate_method_symbol(s.Typ(), method.Name), method.FuncInfo
		}
		typ := field.Type.Typ()
		if (*leftTypName)[0] == '&' {
			left.Kind |= ast2.StructPtr
		}
		*leftTypName = typ
		return errcode.NoErr, nil, typ, nil
	case *ast2.EnumDecl:
		//只对于enumtype.enumelem设置ast2.EnumObj
		//有多个点并涉及枚举类型的选择器，不是有效代码
		if len(v.Slice) != 2 {
			return errcode.SelectorLNoSelectorR, errcode.NewMsgSelectorLNoSelectorR(left.Name, right), "", nil
		}
		v.Slice[0].Kind |= ast2.EnumObj
		if i := indexStr(s.Enums, right); i == -1 {
			return errcode.SelectorLNoSelectorR, errcode.NewMsgSelectorLNoSelectorR(left.Name, right), "", nil
		}
		*leftTypName = s.Name
		return errcode.NoErr, nil, s.Name, nil
	default:
		return errcode.SelectorLNoSelectorR, errcode.NewMsgSelectorLNoSelectorR(left.Name, right), "", nil
	}
}

func checkOpExpr(t *ast2.Tree, n *ast2.OpExpr) (err errcode.ErrCode, msg errcode.Msg, typ string) {
	err, msg, src1 := checkNode(t, n.Src1)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.ASSIGNErr, err)
		return
	}
	err, msg, src2 := checkNode(t, n.Src2)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.ASSIGNErr, err)
		return
	}
	if !utils.Typ_is_equal(src1, src2) { //src1与src2类型不匹配
		return errcode.TypeIsNotEqual, errcode.NewMsgOpexprTypeIsNotEqual(src1, src2), ""
	}
	if utils.IsPtr(src1) && n.OP != enum.EqualOP && n.OP != enum.NoEqualOP { //如果是指针类型，且不是比较相等或不相等
		return errcode.PtrOpOnlyCmp, nil, ""
	}
	switch src1 {
	case enum.String: //如果操作数类型是字符串
		c := ast2.NewCallExpr(strcmp, n.LineNum, n.Src1, n.Src2)
		c.SetNosemicolon(true)
		*n = ast2.OpExpr{
			OP:   enum.EqualOP,
			Src1: c,
			Src2: Num0,
		}
	case enum.Bool: //如果操作数类型是布尔值
		switch n.OP {
		case enum.EqualOP, enum.NoEqualOP, enum.LogicAndOP, enum.LogicOrOP:
		default: //如果进行的运算不是比较相等或不相等或逻辑运算
			return errcode.BoolOpOnlyCmpOrLogic, nil, enum.Bool
		}
	}
	switch n.OP {
	case enum.EqualOP, enum.NoEqualOP, enum.LessOP, enum.GreaterOP: //表达式表示bool值
		return errcode.NoErr, nil, enum.Bool
	}
	return errcode.NoErr, nil, src1
}

var strcmp = ast2.NewObject(ast2.SymbolObj, "strcmp")

var Num0 = ast2.NewObject(ast2.INTOBJ, "0")

func checkFunc(t *ast2.Tree, n *ast2.FuncInfo) {
	for i := range n.Parame {
		err, msg, _ := checkType(t, n.Parame[i].Type)
		if err != errcode.NoErr {
			t.Panic(n.LineNum, msg, errcode.FUNCErr, err)
		}
	}
	for i := range n.RetValue {
		err, msg, _ := checkType(t, n.RetValue[i].Type)
		if err != errcode.NoErr {
			t.Panic(n.LineNum, msg, errcode.FUNCErr, err)
		}
	}
	for i := range n.TypeParame {
		err, msg, _ := checkType(t, n.TypeParame[i].Type)
		if err != errcode.NoErr {
			t.Panic(n.LineNum, msg, errcode.FUNCErr, err)
		}
	}
}

func checkReturn(t *ast2.Tree, v *ast2.ReturnNode) {
	err, msg, typ := checkNode(t, v.RetValue)
	if err != errcode.NoErr {
		t.Panic(v.LineNum, msg, err)
		return
	}
	//Note:下面依赖如果return语句在函数体外，不会进行语义检查
	//如果在函数内
	if len(t.Funcinfo.RetValue) == 0 {
		if v.RetValue != nil { //如果函数或方法没有声明返回值，返回语句却有返回值
			t.Panic(v.LineNum, nil, errcode.ReturnErr, errcode.UndeclRetValutTheRetStmthasRetValue)
		}
	} else {
		decl := t.Funcinfo.RetValue[0].Type.Typ()
		if typ != decl && decl == "" {
			t.Panic(v.LineNum, errcode.NewMsgReturnTypeIsNotEqual(decl, typ), errcode.ReturnErr, errcode.TypeIsNotEqual)
		}
	}
}

func checkIf(t *ast2.Tree, n *ast2.IfNode) {
	checkBoolExpr(t, n.BoolExpr, errcode.IfErr, n.LineNum)
}

func checkBoolExpr(t *ast2.Tree, n ast2.Expr, code errcode.ErrCode, LineNum int) {
	err, msg, typ := checkNode(t, n)
	if err != errcode.NoErr {
		t.Panic(LineNum, msg, code, err)
		return
	}
	if typ != enum.Bool {
		t.Panic(LineNum, nil, code, errcode.ExprNoBool)
	}
}

func checkElse(t *ast2.Tree, n *ast2.ElseNode) {
	if n.BoolExpr == nil {
		return
	}
	checkBoolExpr(t, n.BoolExpr, errcode.ElseErr, n.LineNum)
}

func checkFor(t *ast2.Tree, n *ast2.ForNode) {
	err, msg, _ := checkNode(t, n.InitStmt)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.ForErr, err)
		return
	}
	checkBoolExpr(t, n.BoolExpr, errcode.ForErr, n.LineNum)
	err, msg, _ = checkNode(t, n.InitStmt)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.ForErr, err)
		return
	}
}

// checkSwitch 检查switch语句，如果表达式检查有错误，返回true，表示需要跳过检查switch代码块
func checkSwitch(t *ast2.Tree, n *ast2.SwitchNode) (skip bool) {
	err, msg, typ := checkNode(t, n.Expr)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.SwitchErr, err)
		return true
	}
	t.CheckInfo.SwitchExprTyp = typ
	return false
}

func checkCase(t *ast2.Tree, n *ast2.CaseNode) {
	err, msg, typ := checkNode(t, n.Expr)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.CaseErr, err)
		return
	}
	if typ != t.CheckInfo.SwitchExprTyp {
		t.Panic(n.LineNum, errcode.NewMsgSwitchAndCaseTypNoEqual(t.CheckInfo.SwitchExprTyp, typ), errcode.CaseErr, errcode.TypeIsNotEqual)
	}
}

func checkSelfOpStmt(t *ast2.Tree, n *ast2.SelfOpStmt) {
	err, msg, typ := checkNode(t, n.Dest)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.SelfOpStmtErr, err)
		return
	}
	if typ != enum.Int && typ != enum.Float {
		if utils.IsPtr(typ) {
			t.Panic(n.LineNum, nil, errcode.SelfOpStmtErr, errcode.PtrOpOnlyCmp)
		} else {
			t.Panic(n.LineNum, nil, errcode.SelfOpStmtErr, errcode.DestCannotSelfIncOrDec)
		}
	}
}

func checkIndexExpr(t *ast2.Tree, n *ast2.IndexExpr) (err errcode.ErrCode, elem string) {
	err, msg, typ := checkNode(t, n.Index)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.IndexExprErr, err)
		return ignore, ""
	}
	if typ != enum.Int {
		t.Panic(n.LineNum, msg, errcode.IndexExprErr, errcode.IndexLenMustIsInt)
	}
	err, msg, typ = checkNode(t, n.X)
	if err != errcode.NoErr {
		t.Panic(n.LineNum, msg, errcode.IndexExprErr, err)
		return ignore, ""
	}
	typ, _, ok := utils.Elem(typ)
	if !ok {
		t.Panic(n.LineNum, msg, errcode.IndexExprErr, errcode.IndexExprXErr)
		return ignore, ""
	}
	return errcode.NoErr, typ
}

func indexStr(s []string, str string) int {
	for i := range s {
		if strings.HasSuffix(s[i], str) {
			return i
		}
	}
	return -1
}
